4 // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
6 // The use and distribution terms for this software are contained in the file
7 // named license.txt, which can be found in the root of this distribution.
8 // By using this software in any fashion, you are agreeing to be bound by the
9 // terms of this license.
11 // You must not remove this notice, or any other, from this software.
16 namespace Microsoft
.JScript
{
18 using Microsoft
.JScript
.Vsa
;
20 using System
.Reflection
;
21 using System
.Reflection
.Emit
;
22 using System
.Diagnostics
;
24 public class Equality
: BinaryOp
{
25 private Object metaData
= null;
27 internal Equality(Context context
, AST operand1
, AST operand2
, JSToken operatorTok
)
28 : base(context
, operand1
, operand2
, operatorTok
) {
31 public Equality(int operatorTok
)
32 : base(null, null, null, (JSToken
)operatorTok
){
35 internal override Object
Evaluate(){
36 bool result
= this.EvaluateEquality(this.operand1
.Evaluate(), this.operand2
.Evaluate(), VsaEngine
.executeForJSEE
);
37 if (this.operatorTok
== JSToken
.Equal
)
44 [DebuggerStepThroughAttribute
]
45 [DebuggerHiddenAttribute
]
47 public bool EvaluateEquality(Object v1
, Object v2
){
48 return EvaluateEquality(v1
, v2
, false);
53 [DebuggerStepThroughAttribute
]
54 [DebuggerHiddenAttribute
]
56 private bool EvaluateEquality(Object v1
, Object v2
, bool checkForDebuggerObjects
){
57 if (v1
is String
&& v2
is String
) return v1
.Equals(v2
);
58 if (v1
is Int32
&& v2
is Int32
) return ((int)v1
) == (int)v2
;
59 if (v1
is Double
&& v2
is Double
) return ((double)v1
) == (double)v2
;
60 if ((v2
== null || v2
is DBNull
|| v2
is Missing
) && !checkForDebuggerObjects
) return (v1
== null || v1
is DBNull
|| v1
is Missing
);
61 IConvertible ic1
= Convert
.GetIConvertible(v1
);
62 IConvertible ic2
= Convert
.GetIConvertible(v2
);
63 TypeCode t1
= Convert
.GetTypeCode(v1
, ic1
);
64 TypeCode t2
= Convert
.GetTypeCode(v2
, ic2
);
75 MethodInfo oper
= this.GetOperator(v1
.GetType(), v2
.GetType());
77 bool result
= (bool)oper
.Invoke(null, (BindingFlags
)0, JSBinder
.ob
, new Object
[]{v1, v2}
, null);
78 if (this.operatorTok
== JSToken
.NotEqual
) return !result
;
87 MethodInfo oper
= this.GetOperator(v1
.GetType(), v2
.GetType());
89 bool result
= (bool)oper
.Invoke(null, (BindingFlags
)0, JSBinder
.ob
, new Object
[]{v1, v2}
, null);
90 if (this.operatorTok
== JSToken
.NotEqual
) return !result
;
97 return Equality
.JScriptEquals(v1
, v2
, ic1
, ic2
, t1
, t2
, checkForDebuggerObjects
);
102 public static bool JScriptEquals(Object v1
, Object v2
){
103 if (v1
is String
&& v2
is String
) return v1
.Equals(v2
);
104 if (v1
is Int32
&& v2
is Int32
) return ((int)v1
) == (int)v2
;
105 if (v1
is Double
&& v2
is Double
) return ((double)v1
) == (double)v2
;
106 if ((v2
== null || v2
is DBNull
|| v2
is Missing
)) return (v1
== null || v1
is DBNull
|| v1
is Missing
);
107 IConvertible ic1
= Convert
.GetIConvertible(v1
);
108 IConvertible ic2
= Convert
.GetIConvertible(v2
);
109 TypeCode t1
= Convert
.GetTypeCode(v1
, ic1
);
110 TypeCode t2
= Convert
.GetTypeCode(v2
, ic2
);
111 return Equality
.JScriptEquals(v1
, v2
, ic1
, ic2
, t1
, t2
, false);
114 private static bool JScriptEquals(Object v1
, Object v2
, IConvertible ic1
, IConvertible ic2
, TypeCode t1
, TypeCode t2
, bool checkForDebuggerObjects
){
115 if (StrictEquality
.JScriptStrictEquals(v1
, v2
, ic1
, ic2
, t1
, t2
, checkForDebuggerObjects
))
117 if (t2
== TypeCode
.Boolean
){
118 v2
= ic2
.ToBoolean(null) ? 1 : 0;
119 ic2
= Convert
.GetIConvertible(v2
);
120 return Equality
.JScriptEquals(v1
, v2
, ic1
, ic2
, t1
, TypeCode
.Int32
, false);
123 case TypeCode
.Empty
: return t2
== TypeCode
.Empty
|| t2
== TypeCode
.DBNull
|| (t2
== TypeCode
.Object
&& v2
is Missing
);
124 case TypeCode
.Object
:
127 case TypeCode
.DBNull
:
128 return v1
is Missing
;
133 case TypeCode
.UInt16
:
135 case TypeCode
.UInt32
:
137 case TypeCode
.UInt64
:
138 case TypeCode
.Single
:
139 case TypeCode
.Double
:
140 case TypeCode
.Decimal
:
141 case TypeCode
.String
:
142 IConvertible pvic1
= ic1
;
143 Object pv1
= Convert
.ToPrimitive(v1
, PreferredType
.Either
, ref pvic1
);
144 if (pvic1
!= null && pv1
!= v1
)
145 return Equality
.JScriptEquals(pv1
, v2
, pvic1
, ic2
, pvic1
.GetTypeCode(), t2
, false);
150 case TypeCode
.DBNull
: return t2
== TypeCode
.DBNull
|| t2
== TypeCode
.Empty
|| (t2
== TypeCode
.Object
&& v2
is Missing
);
151 case TypeCode
.Boolean
:
152 v1
= ic1
.ToBoolean(null) ? 1 : 0;
153 ic1
= Convert
.GetIConvertible(v1
);
154 return Equality
.JScriptEquals(v1
, v2
, ic1
, ic2
, TypeCode
.Int32
, t2
, false);
159 case TypeCode
.UInt16
:
161 case TypeCode
.UInt32
:
163 case TypeCode
.UInt64
:
164 case TypeCode
.Single
:
165 case TypeCode
.Double
:
166 case TypeCode
.Decimal
:
167 if (t2
== TypeCode
.Object
){
168 IConvertible pvic2
= ic2
;
169 Object pv2
= Convert
.ToPrimitive(v2
, PreferredType
.Either
, ref pvic2
);
170 if (pvic2
!= null && pv2
!= v2
)
171 return Equality
.JScriptEquals(v1
, pv2
, ic1
, pvic2
, t1
, pvic2
.GetTypeCode(), false);
175 if (t2
== TypeCode
.String
){
176 if (v1
is Enum
) return Convert
.ToString(v1
).Equals(ic2
.ToString(null));
177 v2
= Convert
.ToNumber(v2
, ic2
);
178 ic2
= Convert
.GetIConvertible(v2
);
179 return StrictEquality
.JScriptStrictEquals(v1
, v2
, ic1
, ic2
, t1
, TypeCode
.Double
, false);
182 case TypeCode
.DateTime
:
183 if (t2
== TypeCode
.Object
){
184 IConvertible pvic2
= ic2
;
185 Object pv2
= Convert
.ToPrimitive(v2
, PreferredType
.Either
, ref pvic2
);
186 if (pv2
!= null && pv2
!= v2
)
187 return StrictEquality
.JScriptStrictEquals(v1
, pv2
, ic1
, pvic2
, t1
, pvic2
.GetTypeCode(), false);
190 case TypeCode
.String
:
192 case TypeCode
.Object
:{
193 IConvertible pvic2
= ic2
;
194 Object pv2
= Convert
.ToPrimitive(v2
, PreferredType
.Either
, ref pvic2
);
195 if (pvic2
!= null && pv2
!= v2
)
196 return Equality
.JScriptEquals(v1
, pv2
, ic1
, pvic2
, t1
, pvic2
.GetTypeCode(), false);
203 case TypeCode
.UInt16
:
205 case TypeCode
.UInt32
:
207 case TypeCode
.UInt64
:
208 case TypeCode
.Single
:
209 case TypeCode
.Double
:
210 case TypeCode
.Decimal
:
211 if (v2
is Enum
) return Convert
.ToString(v2
).Equals(ic1
.ToString(null));
212 v1
= Convert
.ToNumber(v1
, ic1
);
213 ic1
= Convert
.GetIConvertible(v1
);
214 return StrictEquality
.JScriptStrictEquals(v1
, v2
, ic1
, ic2
, TypeCode
.Double
, t2
, false);
221 internal override IReflect
InferType(JSField inference_target
){
222 return Typeob
.Boolean
;
225 internal override void TranslateToConditionalBranch(ILGenerator il
, bool branchIfTrue
, Label label
, bool shortForm
){
226 if (this.metaData
== null){
227 Type t1
= this.type1
;
228 Type t2
= this.type2
;
229 Type t3
= Typeob
.Object
;
230 bool emitNullAndUndefined
= true;
231 if (t1
.IsPrimitive
&& t2
.IsPrimitive
){
233 if (t1
== Typeob
.Single
|| t2
== Typeob
.Single
)
235 else if (Convert
.IsPromotableTo(t1
, t2
))
237 else if (Convert
.IsPromotableTo(t2
, t1
))
239 }else if (t1
== Typeob
.String
&& (t2
== Typeob
.String
|| t2
== Typeob
.Empty
|| t2
== Typeob
.Null
)){
241 if (t2
!= Typeob
.String
){
242 emitNullAndUndefined
= false;
243 branchIfTrue
= !branchIfTrue
;
245 }else if ((t1
== Typeob
.Empty
|| t1
== Typeob
.Null
) && t2
== Typeob
.String
){
247 emitNullAndUndefined
= false;
248 branchIfTrue
= !branchIfTrue
;
250 if (t3
== Typeob
.SByte
|| t3
== Typeob
.Int16
)
252 else if (t3
== Typeob
.Byte
|| t3
== Typeob
.UInt16
)
254 if (emitNullAndUndefined
){
255 this.operand1
.TranslateToIL(il
, t3
);
256 this.operand2
.TranslateToIL(il
, t3
);
257 if (t3
== Typeob
.Object
)
258 il
.Emit(OpCodes
.Call
, CompilerGlobals
.jScriptEqualsMethod
);
259 else if (t3
== Typeob
.String
)
260 il
.Emit(OpCodes
.Call
, CompilerGlobals
.stringEqualsMethod
);
261 }else if (t1
== Typeob
.String
)
262 this.operand1
.TranslateToIL(il
, t3
);
263 else if (t2
== Typeob
.String
)
264 this.operand2
.TranslateToIL(il
, t3
);
266 if (this.operatorTok
== JSToken
.Equal
)
267 if (t3
== Typeob
.String
|| t3
== Typeob
.Object
)
268 il
.Emit(shortForm
? OpCodes
.Brtrue_S
: OpCodes
.Brtrue
, label
);
270 il
.Emit(shortForm
? OpCodes
.Beq_S
: OpCodes
.Beq
, label
);
272 if (t3
== Typeob
.String
|| t3
== Typeob
.Object
)
273 il
.Emit(shortForm
? OpCodes
.Brfalse_S
: OpCodes
.Brfalse
, label
);
275 il
.Emit(shortForm
? OpCodes
.Bne_Un_S
: OpCodes
.Bne_Un
, label
);
277 if (this.operatorTok
== JSToken
.Equal
)
278 if (t3
== Typeob
.String
|| t3
== Typeob
.Object
)
279 il
.Emit(shortForm
? OpCodes
.Brfalse_S
: OpCodes
.Brfalse
, label
);
281 il
.Emit(shortForm
? OpCodes
.Bne_Un_S
: OpCodes
.Bne_Un
, label
);
283 if (t3
== Typeob
.String
|| t3
== Typeob
.Object
)
284 il
.Emit(shortForm
? OpCodes
.Brtrue_S
: OpCodes
.Brtrue
, label
);
286 il
.Emit(shortForm
? OpCodes
.Beq_S
: OpCodes
.Beq
, label
);
290 if (this.metaData
is MethodInfo
){
291 MethodInfo oper
= (MethodInfo
)this.metaData
;
292 ParameterInfo
[] pars
= oper
.GetParameters();
293 this.operand1
.TranslateToIL(il
, pars
[0].ParameterType
);
294 this.operand2
.TranslateToIL(il
, pars
[1].ParameterType
);
295 il
.Emit(OpCodes
.Call
, oper
);
297 il
.Emit(shortForm
? OpCodes
.Brtrue_S
: OpCodes
.Brtrue
, label
);
299 il
.Emit(shortForm
? OpCodes
.Brfalse_S
: OpCodes
.Brfalse
, label
);
302 //Getting here is just too bad. We do not know until the code runs whether or not to call an overloaded operator method.
303 //Compile operands to objects and devolve the decision making to run time thunks
304 il
.Emit(OpCodes
.Ldloc
, (LocalBuilder
)this.metaData
);
305 this.operand1
.TranslateToIL(il
, Typeob
.Object
);
306 this.operand2
.TranslateToIL(il
, Typeob
.Object
);
307 il
.Emit(OpCodes
.Call
, CompilerGlobals
.evaluateEqualityMethod
);
309 if (this.operatorTok
== JSToken
.Equal
)
310 il
.Emit(shortForm
? OpCodes
.Brtrue_S
: OpCodes
.Brtrue
, label
);
312 il
.Emit(shortForm
? OpCodes
.Brfalse_S
: OpCodes
.Brfalse
, label
);
314 if (this.operatorTok
== JSToken
.Equal
)
315 il
.Emit(shortForm
? OpCodes
.Brfalse_S
: OpCodes
.Brfalse
, label
);
317 il
.Emit(shortForm
? OpCodes
.Brtrue_S
: OpCodes
.Brtrue
, label
);
322 internal override void TranslateToIL(ILGenerator il
, Type rtype
){
323 Label true_label
= il
.DefineLabel();
324 Label done_label
= il
.DefineLabel();
325 this.TranslateToConditionalBranch(il
, true, true_label
, true);
326 il
.Emit(OpCodes
.Ldc_I4_0
);
327 il
.Emit(OpCodes
.Br_S
, done_label
);
328 il
.MarkLabel(true_label
);
329 il
.Emit(OpCodes
.Ldc_I4_1
);
330 il
.MarkLabel(done_label
);
331 Convert
.Emit(this, il
, Typeob
.Boolean
, rtype
);
334 internal override void TranslateToILInitializer(ILGenerator il
){
335 this.operand1
.TranslateToILInitializer(il
);
336 this.operand2
.TranslateToILInitializer(il
);
337 MethodInfo oper
= this.GetOperator(this.operand1
.InferType(null), this.operand2
.InferType(null));
339 this.metaData
= oper
;
342 if (this.operand1
is ConstantWrapper
){
343 Object val
= this.operand1
.Evaluate();
344 if (val
== null) this.type1
= Typeob
.Empty
;
345 else if (val
is DBNull
) this.type1
= Typeob
.Null
;
347 if (this.operand2
is ConstantWrapper
){
348 Object val
= this.operand2
.Evaluate();
349 if (val
== null) this.type2
= Typeob
.Empty
;
350 else if (val
is DBNull
) this.type2
= Typeob
.Null
;
352 if (this.type1
== Typeob
.Empty
|| this.type1
== Typeob
.Null
|| this.type2
== Typeob
.Empty
|| this.type2
== Typeob
.Null
) return;
353 if ((this.type1
.IsPrimitive
|| this.type1
== Typeob
.String
|| Typeob
.JSObject
.IsAssignableFrom(this.type1
))
354 && (this.type2
.IsPrimitive
|| this.type2
== Typeob
.String
|| Typeob
.JSObject
.IsAssignableFrom(this.type2
)))
356 this.metaData
= il
.DeclareLocal(Typeob
.Equality
);
357 ConstantWrapper
.TranslateToILInt(il
, (int)this.operatorTok
);
358 il
.Emit(OpCodes
.Newobj
, CompilerGlobals
.equalityConstructor
);
359 il
.Emit(OpCodes
.Stloc
, (LocalBuilder
)this.metaData
);